Been several years since working with Swift. This is a refresher and some general notes, starting with the basics. This is focused on Swift v4.
Playground – an Xcode project of a single file that can be executed.
REPL – Read, Eval, Print, Loop; We can run the swift REPL by opening a terminal and typing ‘swift’. The swift REPL is part of the LLDB (the swift debugger). Due to this, the REPL is often a tool used for debugging applications.
Swift is a compiled language. It is not interpreted or intermediate, it must be compiled. There is no runtime environment needed.
The following are variable types
- Int, UInt
- String, Character
- Bool
- Double
- Float
- Array (collection)
- Dictionary (collection)
- Set (collection)
The ‘var’ keyword is used to define variables. All variables are defined using this keyword. Swift uses type inference, meaning as soon as the variable is set Swift will type it to that value. Swift is a type-safe language.
Whenever we use ‘var’, we must either assign the value to it (so swift can infer the type) or we must declare the type. It must have type annotation.
var str = "Hello, playground" str = 22 // Compiler error var score: Int // type annotation, will error if missing
In Swift the accepted style is to use camel case for variable names. The ‘let’ keyword is used to set constant variables. These variables should also use camel case and Swift will infer the data type. If a variable is not mutable, Swift will automatically alert you saying to use ‘let’ instead of ‘var’. We can also use ‘type’ keyword to get the type of a variable.
let pi = 3.14 type(of: pi) // Int.Type let myvar = 1.0 let myvar2: Float? // Optional - this might be type of string or nil let result: Int = pi + myvar // error - coercion not supported let result2 = myvar + myvar2 // error - cannot use optionals without value var unwrap = myvar2! // unwraps the value, but this will runtime error if nil // optional binding if let unwrap2 = myvar2 { // unwraps while avoiding runtime error if nil print (unwrap2) } let convertedResult: Int = Int(pi) + Int(myvar) // 4
All ‘var’ and ‘let’ variables must be initialized before used. Swift does not set default values (like an empty string or 0 for integer). Everything must be initialized first. The exception to this rule is using the “?” optional syntax, which declares the variable as perhaps not used. To unwrap optionals, use optional binding.
Swift does not do implicit conversion for types (coercion). Instead, we need to do conversions when working with different types (explicit conversion).
Collections
The following are core Swift collections:
- Array – ordered collection; zero-based; type-safe, the array must be of same type; let – immutable, var – mutable
- Set – unordered collection;
- Dictionary – key/value pairs
var array1 = ["aaa", "bbb", "ccc"] var array2 = [1, 2, 3, 3, 4, 5] var array3: [String] = [] array1.append("ddd") var result2 = array1.removeFirst()
Flow Control
When working with conditional statements, we dont need parenthesis but curly braces are required. The switch statement is also different than other languages – particularly there is no automatic fall through, no need for break statements, and can use ranges.
let v1: Int = 3 switch v1 { case 0: print("0") case 1: print ("1") case 2: print ("2") case 3...5: print ("3-5") default: break } // Switches using enum var e: en = .m2 switch e { case .m1: print("m1") case .m2: print("m2") case .m3: print("m3") }
Loops the for, repeat-while and while.
for arr in array1 { print(arr) } var x = 1 repeat { x = x + 1 } while (x < 3) while (x < 6) { x = x + 1 }
String Interpolation
var s1 = "Hello World" print ("\(s1)")
Structs
All primative data types – Int, String, Float, etc are of type Struct in Swift (not Object as in other languages). Structs cannot exhibit inheritance. Structs are value types whereas classes are reference types.
struct Movie { var title: String var director: String var year: Int var genre: String func summary() -> String { return "\(title) - \(director)" } } var godfather = Movie(title: "Godfather", director: "Capola", year: 1970, genre: "Drama") print (godfather.summary())
Dictionary
Dictionaries are key/value pairs. The keys must be of same value type, and values are of same type – but the key doesnt have to be the same as the value type.
var airlines = ["SWA": "Southwest Airlines", "BAW": "British Airlines", "BHA": "Buddha Air"] let aa1 = airlines["SWA"] airlines["BHA"] = "Bahama Air" let aa2 = airlines["BHA"] airlines["BAW"] = nil //removed for aa3 in airlines { print (aa3) //(key: "BHA", value: "Bahama Air") } for (code,name) in airlines { //tuple print (name) //"Bahama Air" }
Tuple
func album() -> (title: String, year: Int) { let title = "Galaxy" let year = 1999 return (title, year) } let (albTitle, albYear) = album()
Closures
Closures are heavily used in Swift from general functions working with collections to animation, callbacks, handlers and even interface controls. Closures are reuseable code like functions (a function is actually a type of closure), but are more generic – it doesnt have a name. It is very similar to anonymous functions.
// Closures func compareAirlines(a1: Airline, a2: Airline) -> Bool { if a1.Name <= a2.Name { return true } else { return false } } let sortedAirlines1 = airlines.sorted(by: compareAirlines) let sortedAirlines2 = airlines.sorted(by: { (a1: Airline, a2: Airline) -> Bool // redundant in if a1.Name <= a2.Name { return true } else { return false } }) let sortedAirlines3 = airlines.sorted(by: { if $0.Name <= $1.Name { // implicit reference of Element return true } else { return false } }) let sortedAirlines4 = airlines.sorted { // trailing closure $0.Name <= $1.Name } let filteredAirlines = airlines.filter { $0.Year < 2010 }
Classes and Objects
Swift uses ARC – Automatic Reference Counting – automatically deallocates objects in memory. If there is a ‘deinit’ function it will be called when the object is out of scope.
class Appliance { var name: String = "" var mode: String = "" var capacity: Int? init() { self.name = "defualt" self.mode = "default" self.capacity = 1 } init (name: String) { self.name = name self.mode = "defualt" self.capacity = 1 } deinit { // Perform cleanup, close network or file IO } func getDetails() -> String { var message = "This applicance \(self.name) of mode \(self.mode)" if self.capacity != nil && self.capacity! > 0 { message += " and capcity \(self.capacity ?? 0)" } return message } } var fridge = Appliance() fridge.name = "Fridger" fridge.mode = "Kitchen" fridge.capacity = 5 print (fridge.getDetails()) // This applicance Fridger of mode Kitchen and capcity 5 class Toaster: Appliance { var count: Int override init() { self.count = 0 super.init() } func toast() { print ("Toasting... \(self.name)") } } var toasterz = Toaster() toasterz.toast() // Toasting...default
Note that classes are very similar to structs but can exhibit inheritance. But more importantly, structs are passed by value whereas classes are pass by reference. Therefore when a class is modified it is modified across all parts of the application where it referneced that object. Structs are always copied so modifying it only does so within that scope.
If the class has ‘final’ keyword in front of it, then it cannot be inherited. This can apply to methods as well.
Extensions and Computed Properties
extension String { func removeSpaces() -> String { let filteredChars = self.filter { $0 != " " } return String(filteredChars) } } let t1 = "This is a test of removing spaces" print(t1.removeSpaces()) // Thisisatestofremovingspaces // Computered Properties class Player { var name: String var livesLeft: Int var kills: Int var deaths: Int init() { self.name = "John" self.livesLeft = 3 self.kills = 0 self.deaths = 0 } // computed property var score: Int { get { return (kills * 100 + livesLeft * 10 - deaths * 5) } } } var playerS1 = Player() print (playerS1.score) // 30
Protocols
Swift is a Protocol-Oriented Language. Instead of objects, Swift focuses on protocols. Protocols can be used for general collections, data interactions and UI components. Protocols dont have implementations, only definitions like interfaces. You must implement them. You dont need to write protocols but rather interact with existing ones that are part of the standard library.
Also note that Swift classes allow single class inheritance. Classes, structs and enums also allow multiple protocols. This is how we can make classes adopt methods, properties from other classes.
Adopt a protocol means you are declaring to inherit from an existing protocol. Conform to a protocol means we have implemented the required properties and methods that we are adopting.
protocol myProtocol { var name: String { get set } func showMessage() } struct myStruct: myProtocol { // Adopt the protocol var name: String { // conforming return "MyName" } func showMessage() { // conforming print ("Struct \(self.name) is now conforming...") } } enum ServerError: Error { // adopting the Error protocol case noConnection case serverNotFound case authenticationFailed case unauthorized } func status(num: Int) throws -> String { switch num { case 1: throw ServerError.noConnection case 2: throw ServerError.authenticationFailed case 3: throw ServerError.unauthorized default: throw ServerError.serverNotFound } return "Success!" } do { print ("1") } catch ServerError.noConnection { print ("2") } catch ServerError.unauthorized { print ("3") }
Guard and Defer
if let searchController.isActive = optionalSearchValue .. // isActive can be nil guard searchController.isActive else { return } // Defer func someFunction() { ... defer { // This is always executed, even on error someIO.close() } }
Xcode
Some useful Xcode hints
- CMD + / Automatic code commenting
References
Swift Fundamentals
https://…
CocoaPods.org – Dependency Manager for Swift
https://cocoapods.org/
eof